Machine Language vs High Level Languages
By Andy Stadler
Copyright (c) 1991 Apple Users' Group, Sydney
Republished from Applecations, a publication of the Apple Users' Group, Sydney, Australia.
Software Engineer
Apple Computer, Inc.
Language wars can be very hot and religious. Everybody has a reason to like and dislike the languages they are currently using, the languages they aren't using, and the languages they wish they were using. Let me humbly throw in a few comments from someone who's been there - I've been developing production software at a variety of companies, for the Apple II and other platforms, since the early 1980's.
This statement sums it up best - use the right language for the job.
There have been a lot of arguments about why one language is better than another, especially in the world of the Apple II programmer. I quickly agree that for the 8-bit Apple II, there really is no good well supported high level language (perhaps Apple Pascal but it's too old and no longer actively supported). So I will confine my comments to the Apple IIgs world.
Instead of arguing the merits of particular languages, let's approach this problem from the program design point of view. There are two major concerns a programmer has - to create a program which works correctly; And to create a program which is reasonably efficient (in speed and space). Remember, a program which does the right thing slowly is MUCH more useful than a program which does the wrong thing, no matter how quickly! So it's important to start with a language which allows rapid prototyping, and easy debugging.
Why do I mention prototyping? Because nobody can design the perfect program the first time. I'd much rather have an environment in which I can quickly write three or four proposed implementations of an idea, and then use them for a while (especially if there are others who can try them out). Later, only after I've chosen a specific solution, is the right time for a detailed, correct implementation which I'm going to live with for the future.
So face it, folks, assembly just isn't the language for this. No matter how good a programmer you are, you just can't help but make mistakes when writing assembly code. So you spend a bunch of time fixing them. And because assembly takes so many steps to do things, you spend a lot of your time just sweating the details. It's harder to step back and concentrate on the -big picture-. And not just conceptually - it's a PHYSICAL problem too because you just can't fit as much functionality on a printed page or editor screen.
So let's assume we've written a program, using a high-level language, and it's too slow. What are we going to do? While working on HyperCard IIGS, I learned a very important lesson from Bill Atkinson (author of Apple II Pascal, Quick-Draw, MacPaint, HyperCard, etc). Bill always used to tell me:
"Measure, THEN optimize."
Boy was he right. Time and again I'd look at some code and try to guess why it was slow; Then I'd _measure_ it's performance and discover I was TOTALLY WRONG. Performance and bottlenecks in any moderately complex program are simply UNpredictable. So don't waste your time! Instead, use measurement tools to determine QUANTITATIVELY the real problems. And after a while you'll discover something else.
A program spends 95% of its time in 5% of its code.
Amazingly, this still doesn't mean rewriting in assembly. You could do that, but often there's an alternative. Here's an example. Suppose you discover a routine, in which 40% of your time is being spent. And suppose by some great feat of programming, you pull out all the stops and -double- its speed. You might be real proud of yourself, but stop for a minute. For all that work, you've only sped up your program 20%. Before speeding up any routine, you should first ask yourself "why am I calling it so often!" More often than you might expect, a simple change one or two levels higher can reduce or eliminate calls to a routine. You don't even have to touch the routine because suddenly its speed becomes unimportant. This is another good reason for programming in a high level language, at least for the upper framework of a program - it's MUCH easier to make large-scale LOGICAL changes often with great performance benefits. -Concentrate on algorithms, not implementation.-
Of course, when all else fails, take that time hog and rewrite it. You do use standard calling sequences, right? [Sub Editor's note: Look out for the article next month about calling procedures and stack frame macros] If you've set everything up correctly, you should be able to rewrite the module and simply drop it in with no change to any other portions of your program....
One thing you may find surprising is how little of your program will need rewriting to gather great speed increases. Let me quote some statistics from the authors of the original AT&T unix system: "The UNIX kernel consists of about 10,000 lines of C code and about 1,000 lines of assembly code. The assembly code can further be broken down into 200 lines included for the sake of efficiency (the could have been written in C) and 800 lines to perform hardware functions not possible in C." That's right - less than TWO PERCENT of the unix kernel was moved to assembly for speed reasons.
So, in summary, I believe that a program will be written with the best design, quickest implementation, fewest bugs, and best performance, if you use the following steps:
a. design, prototype, implement, and begin testing, using a high level language
b. begin debugging effort
c. begin performance analysis
d. identify key bottleneck routines
e. rewrite upper level algorithms to avoid bottlenecks
f. rewrite remaining bottlenecks in assembly
g. continue debugging and tuning
By the way, don't get the idea that I'm totally against writing original code in assembly language. Some functions, especially graphics and certain types of data manipulation (like string manipulation) are clearly destined to be written in assembly, and it would be a waste of time to write in high level. But that's about it.
What about C versus Pascal? Well, it would be nice if we could freely choose between them, but in the limited set of compilers available for the IIGS we can't. If you are using MPW IIGS, surprisingly Pascal is the best choice because PascalIIGS is a very good compiler and CIIGS is pretty crummy. On APW, however, the ORCA compilers are both pretty good and you might find C a better choice for portability's sake.
Permission is hereby granted for non-profit user groups to republish this content. PLEASE CREDIT THE AUTHOR AND THE SOURCE: Applecations, publication of the Apple Users' Group, Sydney, Australia